// This file is part of Keepass2Android, Copyright 2025 Philipp Crocoll.
//
//   Keepass2Android is free software: you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   Keepass2Android is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU General Public License for more details.
//
//   You should have received a copy of the GNU General Public License
//   along with Keepass2Android.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using Android.App;
using Android.Content;
using KeePassLib;

namespace keepass2android
{
  public abstract class DeleteRunnable : OperationWithFinishHandler
  {
    protected DeleteRunnable(OnOperationFinishedHandler operationFinishedHandler, IKp2aApp app)
        : base(app, operationFinishedHandler)
    {
      App = app;
    }

    protected IKp2aApp App;

    protected Database Db;



    protected void SetMembers(Database db)
    {
      Db = db;
    }


    private bool _deletePermanently = true;

    public bool DeletePermanently
    {
      get
      {
        return _deletePermanently;
      }
      set
      {
        _deletePermanently = value;
      }
    }

    public abstract bool CanRecycle
    {
      get;
    }

    protected bool CanRecycleGroup(PwGroup pgParent)
    {
      PwDatabase pd = Db.KpDatabase;
      PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);
      bool bPermanent = false;
      if (pgParent != null)
      {
        if (pd.RecycleBinEnabled == false)
        {
          Android.Util.Log.Debug("KP2A", "CanRecycle? No, RecycleBinIsNotEnabled");
          bPermanent = true;
        }

        else if (pgRecycleBin == null)
        {
        } // Recycle
        else if (pgParent == pgRecycleBin)
        {
          Android.Util.Log.Debug("KP2A", "CanRecycle? No, Can't recycle RecycleBin");
          bPermanent = true;
        }

        else if (pgParent.IsContainedIn(pgRecycleBin))
        {
          Android.Util.Log.Debug("KP2A", "CanRecycle? No, " + pgParent.Name + " is in RecycleBin");
          bPermanent = true;
        }

      }
      return !bPermanent;
    }


    protected void EnsureRecycleBinExists(ref PwGroup pgRecycleBin,
                                        ref bool bGroupListUpdateRequired)
    {
      if ((Db == null) || (Db.KpDatabase == null)) { return; }

      if (pgRecycleBin == Db.KpDatabase.RootGroup)
      {
        pgRecycleBin = null;
      }

      if (pgRecycleBin == null)
      {
        pgRecycleBin = new PwGroup(true, true, App.GetResourceString(UiStringKey.RecycleBin),
                                   PwIcon.TrashBin)
        {
          EnableAutoType = false,
          EnableSearching = false,
          IsExpanded = false
        };

        Db.KpDatabase.RootGroup.AddGroup(pgRecycleBin, true);
        Db.GroupsById[pgRecycleBin.Uuid] = pgRecycleBin;
        Db.Elements.Add(pgRecycleBin);
        Db.KpDatabase.RecycleBinUuid = pgRecycleBin.Uuid;

        bGroupListUpdateRequired = true;
      }
      else { System.Diagnostics.Debug.Assert(pgRecycleBin.Uuid.Equals(Db.KpDatabase.RecycleBinUuid)); }
    }

    protected abstract UiStringKey QuestionRecycleResourceId
    {
      get;
    }

    protected abstract UiStringKey QuestionNoRecycleResourceId
    {
      get;
    }


    public void Start()
    {
      string messageSuffix = ShowDatabaseIocInStatus ? "(" + App.GetFileStorage(Db.Ioc).GetDisplayName(Db.Ioc) + ")" : "";

      if (CanRecycle)
      {
        App.AskYesNoCancel(UiStringKey.AskDeletePermanently_title,
            QuestionRecycleResourceId,
            (dlgSender, dlgEvt) =>
            {
              DeletePermanently = true;
              BlockingOperationStarter pt = new BlockingOperationStarter(App, this);
              pt.Run();

            },
        (dlgSender, dlgEvt) =>
        {
          DeletePermanently = false;
          BlockingOperationStarter pt = new BlockingOperationStarter(App, this);
          pt.Run();
        },
        (dlgSender, dlgEvt) => { },
        messageSuffix);



      }
      else
      {
        App.AskYesNoCancel(UiStringKey.AskDeletePermanently_title,
            QuestionNoRecycleResourceId,
            (dlgSender, dlgEvt) =>
            {
              BlockingOperationStarter pt = new BlockingOperationStarter(App, this);
              pt.Run();
            },
        null,
        (dlgSender, dlgEvt) => { },
         messageSuffix);


      }
    }


    protected void DoDeleteEntry(PwEntry pe, List<PwGroup> touchedGroups)
    {
      PwDatabase pd = Db.KpDatabase;

      PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);

      bool bUpdateGroupList = false;
      DateTime dtNow = DateTime.Now;

      PwGroup pgParent = pe.ParentGroup;
      if (pgParent != null)
      {
        pgParent.Entries.Remove(pe);
        //TODO check if RecycleBin is deleted
        //TODO no recycle bin in KDB

        if ((DeletePermanently) || (!CanRecycle))
        {
          PwDeletedObject pdo = new PwDeletedObject(pe.Uuid, dtNow);
          pd.DeletedObjects.Add(pdo);
          touchedGroups.Add(pgParent);
          Db.EntriesById.Remove(pe.Uuid);
          Db.Elements.Remove(pe);
        }
        else // Recycle
        {
          EnsureRecycleBinExists(ref pgRecycleBin, ref bUpdateGroupList);

          pgRecycleBin.AddEntry(pe, true, true);
          pe.Touch(false);

          touchedGroups.Add(pgParent);
          // Mark new parent dirty
          touchedGroups.Add(pgRecycleBin);
          // mark root dirty if recycle bin was created
          touchedGroups.Add(Db.Root);
        }
      }
    }


    public override void Run()
    {
      StatusLogger.UpdateMessage(StatusMessage);

      List<PwGroup> touchedGroups = new List<PwGroup>();
      List<PwGroup> permanentlyDeletedGroups = new List<PwGroup>();
      Android.Util.Log.Debug("KP2A", "Calling PerformDelete..");
      PerformDelete(touchedGroups, permanentlyDeletedGroups);

      _operationFinishedHandler = new ActionOnOperationFinished(App, (success, message, context) =>
      {
        if (success)
        {
          foreach (var g in touchedGroups)
            App.DirtyGroups.Add(g);
          foreach (var g in permanentlyDeletedGroups)
          {
            //remove groups from global lists if present there
            App.DirtyGroups.Remove(g);
            Db.GroupsById.Remove(g.Uuid);
            Db.Elements.Remove(g);

          }

        }
        else
        {
          // Let's not bother recovering from a failure to save.  It is too much work.
          App.Lock(false, false);
        }
      }, operationFinishedHandler);

      // Commit database
      SaveDb save = new SaveDb(App, Db, operationFinishedHandler, false, null);
      save.ShowDatabaseIocInStatus = ShowDatabaseIocInStatus;

      save.SetStatusLogger(StatusLogger);
      save.Run();


    }

    public bool ShowDatabaseIocInStatus
    {
      get;
      set;
    }

    protected abstract void PerformDelete(List<PwGroup> touchedGroups, List<PwGroup> permanentlyDeletedGroups);

    public abstract UiStringKey StatusMessage { get; }

    protected bool DoDeleteGroup(PwGroup pg, List<PwGroup> touchedGroups, List<PwGroup> permanentlyDeletedGroups)
    {
      PwGroup pgParent = pg.ParentGroup;
      if (pgParent == null) return false;

      PwDatabase pd = Db.KpDatabase;
      PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);

      if (pg.Uuid.Equals(pd.EntryTemplatesGroup))
      {
        pd.EntryTemplatesGroup = PwUuid.Zero;
        pd.EntryTemplatesGroupChanged = DateTime.Now;
      }

      pgParent.Groups.Remove(pg);
      touchedGroups.Add(pgParent);
      if ((DeletePermanently) || (!CanRecycle))
      {
        pg.DeleteAllObjects(pd);

        PwDeletedObject pdo = new PwDeletedObject(pg.Uuid, DateTime.Now);
        pd.DeletedObjects.Add(pdo);


        permanentlyDeletedGroups.Add(pg);

      }
      else // Recycle
      {
        bool groupListUpdateRequired = false;
        EnsureRecycleBinExists(ref pgRecycleBin, ref groupListUpdateRequired);

        pgRecycleBin.AddGroup(pg, true, true);
        pg.Touch(false);
        // Mark new parent (Recycle bin) touched
        touchedGroups.Add(pg.ParentGroup);
        // mark root touched if recycle bin was created
        if (groupListUpdateRequired)
          touchedGroups.Add(Db.Root);
      }
      return true;
    }
  }
}

